#ifndef SCENE_H
#define SCENE_H
#include <vector>
#include <memory>
#include "GL/glew.h"
#include "shader.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <assimp/quaternion.h>
struct Weight {
    unsigned int indexid;
    float weight;
};

class Bone {
public:
    Bone(){}
    std::string mName;
    uint32_t mCountWeight;
    Weight* mWeight;
    glm::mat4 mOffsetMat;
};

template <class T>
class Vector4 {
public:
    Vector4(){}
    Vector4(T _x) {
        x = y = z = w = _x;
    }
    Vector4(T _x, T _y, T _z,T _w) {
        x = _x;
        y = _y;
        z = _z;
        w = _w;
    }
    T& operator[] (unsigned int index) {
        switch (index)
        {
        case 0:
            return x;
            break;
        case 1:
            return y;
            break;
        case 2:
            return z;
            break;
        case 3:
            return w;
            break;
        default:
            break;
        }
    }
    T x, y, z, w;
};

template <class T>
class Vector3 {
public:
    Vector3() {}
    Vector3(T _x) {
        x = y = z = _x;
    }
    Vector3(T _x,T _y,T _z){
        x = _x;
        y = _y;
        z = _z;
    }
    T& operator[] (unsigned int index) {
        switch (index)
        {
        case 0:
            return x;
            break;
        case 1:
            return y;
            break;
        case 2:
            return z;
            break;
        default:
            break;
        }
    }

    T x, y, z;
};
typedef Vector3<float> Vector3f;
typedef Vector4<float> Vector4f;

struct Vertex
{
    Vector3f position = {0.0f};
    Vector3f color = {0.0f};
    Vector4f weight = { 0.0f };
    Vector4f bone={-1};
};

class Mesh {
public:
    Mesh(){}
    std::vector<Vertex> mVertices;
    std::vector<Bone> mBones;
};

struct Key
{
    float time;
};

struct TranslateKey : Key {
    TranslateKey(float _time, Vector3<float> value) {
        time = _time;
        mValue = value;
    }
    Vector3<float> mValue;
};
struct ScaleKey : Key {
    Vector3<float> mValue;
    ScaleKey(float _time, Vector3<float> value) {
        time = _time;
        mValue = value;
    }
};
struct RotateKey : Key {
    RotateKey(float _time, Vector4<float> value){
        time = _time;
        mValue = value;
    }
    Vector4<float> mValue;
};

class Animation {
public:
    Animation() {
    }
    
    std::string mNodeName;
    uint32_t mNumKeys;
    std::vector<TranslateKey> mTranslateKey;
    std::vector<ScaleKey> mScaleKey;
    std::vector<RotateKey> mRotateKey;
    TranslateKey getTranslateKey(float time) {
        int beforekey = 0, afterkey = 0;
        for (int i = 0; i < mNumKeys-1; i++)
        {
            if (mTranslateKey[i].time <= time&&mTranslateKey[i + 1].time > time)
            {
                beforekey = i;
                afterkey = i + 1;
            }
        }
        auto vector3 = mTranslateKey[beforekey].mValue;
        glm::vec3 A{ vector3.x,vector3.y,vector3.z };
        vector3 = mTranslateKey[afterkey].mValue;
        glm::vec3 B{ vector3.x,vector3.y,vector3.z };
        glm::vec3 C = A + (B - A)*(time - mTranslateKey[beforekey].time) / (mTranslateKey[afterkey].time - mTranslateKey[beforekey].time);
        TranslateKey translate{ time,Vector3f{C.x,C.y,C.z} };
        return translate;
    }
    ScaleKey getScaleKey(float time) {
        int beforekey = 0, afterkey = 0;
        for (int i = 0; i < mNumKeys - 1; i++)
        {
            if (mScaleKey[i].time <= time&&mScaleKey[i + 1].time > time)
            {
                beforekey = i;
                afterkey = i + 1;
            }
        }
        auto vector3 = mScaleKey[beforekey].mValue;
        glm::vec3 A{ vector3.x,vector3.y,vector3.z };
        vector3 = mScaleKey[afterkey].mValue;
        glm::vec3 B{ vector3.x,vector3.y,vector3.z };
        glm::vec3 C = A + (B - A)*(time - mScaleKey[beforekey].time) / (mScaleKey[afterkey].time - mScaleKey[beforekey].time);
        ScaleKey scale{ time,Vector3f{ C.x,C.y,C.z } };
        return scale;
    }
    RotateKey getRotateKey(float time) {
        int beforekey = 0, afterkey = 0;
        for (int i = 0; i < mNumKeys - 1; i++)
        {
            if (mRotateKey[i].time <= time && mRotateKey[i + 1].time > time)
            {
                beforekey = i;
                afterkey = i + 1;
            }
        }
        auto vector4 = mRotateKey[beforekey].mValue;
        glm::quat A{ vector4.w, vector4.x,vector4.y,vector4.z };
        vector4 = mRotateKey[afterkey].mValue;
        glm::quat B{ vector4.w,vector4.x,vector4.y,vector4.z };
        float factor = (time - mRotateKey[beforekey].time) / (mRotateKey[afterkey].time - mRotateKey[beforekey].time);
        glm::quat Out = glm::mix(A, B, factor);

        RotateKey rotate{ time,Vector4f{ Out.x,Out.y,Out.z, Out.w} };
        return rotate;
    }

};

class Object {
public:
    Object(){}
    ~Object(){}
    std::string mName;
};

class Node :public Object {
public:
    Node(){}
    ~Node(){}
    std::vector<std::shared_ptr<Node>> mChildren;
    glm::mat4 mTransform;
    std::vector<Object*> mParent;
};

class Scene {
public:
    Scene();
    ~Scene();

    GLuint mVBO, mVAO;
    Shader mShader;

    std::shared_ptr<Node> mNode;

    std::vector<Mesh> mMesh;
    std::vector<Animation> mAnimation;
};

#endif // !SCENE_H
